import { __ } from '@wordpress/i18n'; import { external, Icon } from '@wordpress/icons'; export const Support = ({ height }) => { if (!window.extHelpCenterData?.supportUrl) { return
; } return (
{__( 'For other questions, visit our support page.', 'extendify-local', )}
); }; import apiFetch from '@wordpress/api-fetch'; import { safeParseJson } from '@shared/lib/parsing'; import { create } from 'zustand'; import { devtools, persist, createJSONStorage } from 'zustand/middleware'; const key = 'extendify-help-center-tour-progress'; const startingState = { currentTour: null, currentStep: undefined, preparingStep: undefined, progress: [], // initialize the state with default values ...(safeParseJson(window.extHelpCenterData.userData.tourData)?.state ?? {}), }; const state = (set, get) => ({ ...startingState, startTour: async (tourData) => { const { trackTourProgress, updateProgress, getStepData, onTourPage } = get(); if (onTourPage(tourData?.settings?.startFrom)) { await tourData?.onStart?.(tourData); tourData.steps = tourData.steps?.filter( // Filter out steps that define a condition (s) => s?.showOnlyIf?.() || s?.showOnlyIf?.() === undefined, ) || []; await getStepData(0, tourData)?.events?.beforeAttach?.(tourData); } set({ currentTour: tourData, currentStep: 0, preparingStep: undefined }); // Increment the opened count const tour = trackTourProgress(tourData.id); updateProgress(tour.id, { openedCount: Number(tour.openedCount) + 1, lastAction: 'started', }); }, onTourPage: (startFrom = null) => { const url = window.location.href; if (startFrom?.includes(url)) return true; const { currentTour } = get(); return currentTour?.settings?.startFrom?.includes(url); }, completeCurrentTour: async () => { const { currentTour, wasCompleted, findTourProgress, updateProgress } = get(); const tour = findTourProgress(currentTour?.id); if (!tour?.id) return; // if already completed, don't update the completedAt if (!wasCompleted(tour.id)) { updateProgress(tour.id, { completedAt: new Date().toISOString(), lastAction: 'completed', }); } // Track how many times it was completed updateProgress(tour.id, { completedCount: Number(tour.completedCount) + 1, lastAction: 'completed', }); await currentTour?.onDetach?.(); await currentTour?.onFinish?.(); set({ currentTour: null, currentStep: undefined }); // fire an event to update the site assistant tour status in assistant code base. if (tour?.id === 'site-assistant-tour') { window.dispatchEvent( new CustomEvent('extendify-assist:is-tour-finished', { detail: { isFinished: true }, }), ); } }, closeCurrentTour: async (lastAction) => { const { currentTour, findTourProgress, updateProgress } = get(); const tour = findTourProgress(currentTour?.id); if (!tour?.id) return; const additional = {}; if (['redirected'].includes(lastAction)) { return updateProgress(tour?.id, { lastAction }); } if (['closed-by-caught-error'].includes(lastAction)) { return updateProgress(tour?.id, { lastAction, errored: true }); } if (lastAction === 'closed-manually') { additional.closedManuallyCount = Number(tour.closedManuallyCount) + 1; } await currentTour?.onDetach?.(); await currentTour?.onFinish?.(); updateProgress(tour?.id, { lastAction, ...additional }); set({ currentTour: null, currentStep: undefined, preparingStep: undefined, }); }, findTourProgress: (tourId) => get().progress.find((tour) => tour.id === tourId), wasCompleted: (tourId) => get().findTourProgress(tourId)?.completedAt, wasOpened: (tourId) => Number(get().findTourProgress(tourId)?.openedCount ?? 0) > 0, isSeen: (tourId) => get().findTourProgress(tourId)?.firstSeenAt, trackTourProgress: (tourId) => { const { findTourProgress } = get(); // If we are already tracking it, return that if (findTourProgress(tourId)) { return findTourProgress(tourId); } set((state) => ({ progress: [ ...state.progress, { id: tourId, firstSeenAt: new Date().toISOString(), updatedAt: new Date().toISOString(), completedAt: null, lastAction: 'init', currentStep: 0, openedCount: 0, closedManuallyCount: 0, completedCount: 0, errored: false, }, ], })); return findTourProgress(tourId); }, updateProgress: (tourId, update) => { const lastAction = update?.lastAction ?? 'unknown'; set((state) => { const progress = state.progress.map((tour) => { if (tour.id === tourId) { return { ...tour, ...update, lastAction, updatedAt: new Date().toISOString(), }; } return tour; }); return { progress }; }); }, getStepData: (step, tour = get().currentTour) => tour?.steps?.[step] ?? {}, hasNextStep: () => { if (!get().currentTour) return false; return Number(get().currentStep) < get().currentTour.steps.length - 1; }, nextStep: async () => { const { currentTour, goToStep, updateProgress, currentStep } = get(); const step = Number(currentStep) + 1; await goToStep(step); updateProgress(currentTour.id, { currentStep: step, lastAction: 'next', }); }, hasPreviousStep: () => { if (!get().currentTour) return false; return Number(get().currentStep) > 0; }, prevStep: async () => { const { currentTour, goToStep, updateProgress, currentStep } = get(); const step = currentStep - 1; await goToStep(step); updateProgress(currentTour.id, { currentStep: step, lastAction: 'prev', }); }, goToStep: async (step) => { const { currentTour, updateProgress, closeCurrentTour, getStepData } = get(); const tour = currentTour; // Check that the step is valid if (step < 0 || step > tour.steps.length - 1) { closeCurrentTour('closed-by-caught-error'); return; } updateProgress(tour.id, { currentStep: step, lastAction: `go-to-step-${step}`, }); const events = getStepData(step)?.events; if (events?.beforeAttach) { set(() => ({ preparingStep: step })); // Make sure the preparing animation runs at least 300ms await Promise.allSettled([ events.beforeAttach?.(tour), new Promise((resolve) => setTimeout(resolve, 300)), ]); set(() => ({ preparingStep: undefined })); } set(() => ({ currentStep: step })); }, }); const path = '/extendify/v1/help-center/tour-data'; const storage = { getItem: async () => await apiFetch({ path }), setItem: async (_name, state) => await apiFetch({ path, method: 'POST', data: { state } }), }; export const useTourStore = create( persist(devtools(state, { name: 'Extendify Tour Progress' }), { name: key, storage: createJSONStorage(() => storage), skipHydration: true, partialize: (state) => { // return without currentTour or currentStep // eslint-disable-next-line no-unused-vars const { currentTour, currentStep, preparingStep, ...newState } = state; return newState; }, }), ); import BarChart from './BarChart'; import Checkmark from './Checkmark'; import Design from './Design'; import Donate from './Donate'; import LeftCaret from './LeftCaret'; import Logo from './Logo'; import Monetization from './Monetization'; import OpenEnvelope from './OpenEnvelope'; import Pencil from './Pencil'; import Planner from './Planner'; import PreviewIcon from './PreviewIcon'; import PriceTag from './PriceTag'; import Radio from './Radio'; import RefreshIcon from './RefreshIcon'; import RightCaret from './RightCaret'; import School from './School'; import SearchIcon from './SearchIcon'; import Shop from './Shop'; import Speech from './Speech'; import Spinner from './Spinner'; import SpinnerIcon from './SpinnerIcon'; import Ticket from './Ticket'; export { BarChart, Checkmark, Design, Donate, LeftCaret, Logo, Monetization, OpenEnvelope, Pencil, Planner, PreviewIcon, PriceTag, Radio, School, RefreshIcon, RightCaret, SearchIcon, Shop, Speech, Spinner, SpinnerIcon, Ticket, }; {"code":"rest_no_route","message":"Nenhuma rota foi encontrada que corresponde com o URL e o m\u00e9todo de requisi\u00e7\u00e3o.","data":{"status":404}}